package graph.algorithms.maxflow;

import graph.algorithms.Algorithm;
import graph.datastructure.Queue;
import graph.views.RunningForm.AlgorithmTextPanel;

public class FordFulkerson extends Algorithm {
	private boolean[] marked; // marked[v] = true iff s->v path in residual
								// graph
	private FlowEdge[] edgeTo; // edgeTo[v] = last edge on shortest residual
								// s->v path
	private double value; // current value of max flow

	// max flow in flow network G from s to t
	public FordFulkerson(FlowNetwork G, int s, int t) {

		// while there exists an augmenting path, use it
		while (hasAugmentingPath(G, s, t)) {

			// compute bottleneck capacity
			double bottle = Double.POSITIVE_INFINITY;
			for (int v = t; v != s; v = edgeTo[v].other(v)) {
				bottle = Math.min(bottle, edgeTo[v].residualCapacityTo(v));
			}

			// augment flow
			for (int v = t; v != s; v = edgeTo[v].other(v)) {
				edgeTo[v].addResidualFlowTo(v, bottle);
			}

			value += bottle;
		}

	}

	// return value of max flow
	public double value() {
		return value;
	}

	// is v in the s side of the min s-t cut?
	public boolean inCut(int v) {
		return marked[v];
	}

	// return an augmenting path if one exists, otherwise return null
	private boolean hasAugmentingPath(FlowNetwork G, int s, int t) {
		edgeTo = new FlowEdge[G.V()];
		marked = new boolean[G.V()];

		// breadth-first search
		Queue<Integer> q = new Queue<Integer>();
		q.enqueue(s);
		marked[s] = true;
		while (!q.isEmpty()) {
			int v = q.dequeue();

			for (FlowEdge e : G.adj(v)) {
				int w = e.other(v);

				// if residual capacity from v to w
				if (e.residualCapacityTo(w) > 0) {
					if (!marked[w]) {
						edgeTo[w] = e;
						marked[w] = true;
						q.enqueue(w);
					}
				}
			}
		}

		// is there an augmenting path?
		return marked[t];
	}

}
